package com.navinfo.tripanalysis.offline.service;

import com.navinfo.tripanalysis.common.util.CommonUtils;
import com.navinfo.tripanalysis.common.util.DateUtils;
import com.navinfo.tripanalysis.common.pojo.Point0F37;
import com.navinfo.tripanalysis.common.pojo.Point0200;
import com.navinfo.tripanalysis.common.pojo.Point;
import com.navinfo.tripanalysis.offline.pojo.PreTripInfo;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 行程切分接口<br/>
 * 通过0200和0F37数据切分行程
 * @author web
 */
public interface PreTripInfoService extends Serializable {
    /**
     * 行程未开始
     */
    int TRIP_NO_START = 0;
    /**
     * 行程开始
     */
    int TRIP_START = 1;
    /**
     * 行程中
     */
    int TRIP_IN = 2;
    /**
     * 行程结束
     */
    int TRIP_END = 3;
    /**
     * 行程强制结束，两个位置点间隔大于FORCE_END_DURATION_THRESHOLD标记为强制结束
     */
    int TRIP_FORCE_END = 4;
    /**
     * 强制结束行程时长阀值：10分钟
     * 单位:MS
     */
    long FORCE_END_DURATION_THRESHOLD = 10 * 60 * 1000L;

    /**
     * 根据0200或0F37切分行程
     * @param points0f37
     * @param points0200
     * @param day        当前日期
     * @return
     */
    List<PreTripInfo> preJudgeTrip(List<Point> points0f37, List<Point0200> points0200, Date day);


    /**
     * 根据公共轨迹协议来切分行程点
     * @param points0200  公共轨迹协议
     * @param day
     * @return
     */
    default List<PreTripInfo> judgeTrip(List<Point> points0f37, List<Point0200> points0200, Date day){
        List<PreTripInfo> tripList = new ArrayList<>(100);
        Map<Integer, List<Point0200>> pointMap = points0200.stream().collect(Collectors.groupingBy(Point0200::getTripId));
        Integer maxTripId = pointMap.keySet().stream().max(Comparator.comparingInt(o -> o)).get();

        pointMap.forEach((tripId, point0200s) -> {
                PreTripInfo preTripInfo = new PreTripInfo();
                preTripInfo.setTripId(tripId);
                preTripInfo.setSubPoints0200(point0200s);

                //排序0200位置点
                point0200s.sort(Point.COMPARATOR);
                Point0200 firstPoint = point0200s.get(0);
                preTripInfo.setStartTime(firstPoint.getGpsTime());
                preTripInfo.setStartLongitude(firstPoint.getOriginalLng());
                preTripInfo.setStartLatitude(firstPoint.getOriginalLat());
                preTripInfo.setStartHeight((int)firstPoint.getHeight());
                preTripInfo.setStartDirection(firstPoint.getDirection());
                preTripInfo.setStartOilPercentage(firstPoint.getOilPercentage()*100);

                Point0200 lastPoint = point0200s.get(point0200s.size()-1);
                preTripInfo.setEndTime(lastPoint.getGpsTime());
                preTripInfo.setEndLongitude(lastPoint.getOriginalLng());
                preTripInfo.setEndLatitude(lastPoint.getOriginalLat());
                preTripInfo.setEndHeight((int)lastPoint.getHeight());
                preTripInfo.setEndDirection(lastPoint.getDirection());
                preTripInfo.setEndOilPercentage(lastPoint.getOilPercentage()*100);

                //是否强制结束
                if (maxTripId==tripId && isForceEnd(lastPoint.getGpsTime(), DateUtils.addDays(day, 1).getTime())) {
                    preTripInfo.setForceEndFlag(1);
                } else {
                    preTripInfo.setForceEndFlag(0);
                }

                //添加相关的0F37点
                if (CommonUtils.isCollectionNotEmpty(points0f37)) {
                    preTripInfo.setSubPoints0f37(points0f37.stream().filter(p -> tripId==p.getTripId() && p.getGpsTime() >= preTripInfo.getStartTime() && p.getGpsTime() <= preTripInfo.getEndTime()).collect(Collectors.toList()));
                }
                tripList.add(preTripInfo);
            });


        return tripList;
    }

    /**
     * 当前时间-上一个时间超过指定阀值，强制结束行程
     *
     * @param lastGpsTime 上一个Point时间点
     * @param gpsTime     当前Point时间点
     * @return
     */
    default boolean isForceEnd(long lastGpsTime, long gpsTime) {
        return (gpsTime - lastGpsTime) > FORCE_END_DURATION_THRESHOLD;
    }

    /**
     * 行程开始处理
     * @param tripId 前一个行程id
     * @param pojo 0200位置点
     * @return
     */
    default PreTripInfo handleStart(int tripId, Point0200 pojo){
        PreTripInfo preTripInfo = new PreTripInfo();
        preTripInfo.setTripId(tripId);
        preTripInfo.setStartTime(pojo.getGpsTime());
        preTripInfo.setStartLongitude(pojo.getOriginalLng());
        preTripInfo.setStartLatitude(pojo.getOriginalLat());
        preTripInfo.setStartHeight((int)pojo.getHeight());
        preTripInfo.setStartDirection(pojo.getDirection());
        preTripInfo.setSubPoints0200(new ArrayList<>());
        return preTripInfo;
    }

    /**
     * 行程结束的处理
     * @param points0f37
     * @param list
     * @param preTripInfo
     * @param status      为null时，为一天所有行程最后的状态处理
     * @param day
     */
    default void handleEnd(List<Point> points0f37, List<PreTripInfo> list, PreTripInfo preTripInfo, Integer status, Date day) {
        //没有行程信息直接返回
        if (preTripInfo == null) {
            return;
        }

        List<Point0200> subList0200 = preTripInfo.getSubPoints0200();

        //没有0200点信息直接返回
        if (CommonUtils.isCollectionEmpty(subList0200)) {
            return;
        }

        //一天最后一个点的gps时间（最大）
        Point0200 last0200 = subList0200.get(subList0200.size() - 1);
        preTripInfo.setEndTime(last0200.getGpsTime());
        preTripInfo.setEndLatitude(last0200.getOriginalLat());
        preTripInfo.setEndLongitude(last0200.getOriginalLng());
        preTripInfo.setEndHeight((int)last0200.getHeight());
        preTripInfo.setEndDirection(last0200.getDirection());


        //一天所有行程最后的状态处理
        if (status == null) {
            status = isForceEnd(last0200.getGpsTime(), DateUtils.addDays(day, 1).getTime()) ? TRIP_FORCE_END : TRIP_END;
        }

        if (status == TRIP_FORCE_END) {
            preTripInfo.setForceEndFlag(1);
        } else {
            preTripInfo.setForceEndFlag(0);
        }

        //添加相关的0F37点
        if (CommonUtils.isCollectionNotEmpty(points0f37)) {
            preTripInfo.setSubPoints0f37(points0f37.stream().filter(p -> (p.getGpsTime() >= preTripInfo.getStartTime()) && (p.getGpsTime() <= preTripInfo.getEndTime())).map(p -> {
                ((Point0F37) p).setTripId(preTripInfo.getTripId());
                return p;
            }).collect(Collectors.toList()));
        }

        list.add(preTripInfo);
    }
}
